/*
 * Copyright (C) 
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "vault_of_archavon.h"
#include "Battlefield.h"
#include "BattlefieldMgr.h"
#include "SpellAuras.h"
#include "Player.h"

/* Vault of Archavon encounters:
1 - Archavon the Stone Watcher event
2 - Emalon the Storm Watcher event
3 - Koralon the Flame Watcher event
4 - Toravon the Ice Watcher event
*/

class instance_vault_of_archavon : public InstanceMapScript
{
    public:
        instance_vault_of_archavon() : InstanceMapScript("instance_vault_of_archavon", 624) { }

        struct instance_vault_of_archavon_InstanceMapScript : public InstanceScript
        {
            instance_vault_of_archavon_InstanceMapScript(Map* map) : InstanceScript(map)
            {
            }

            void Initialize()
            {
				memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
				memset(&bossGUIDs, 0, sizeof(bossGUIDs));

                ArchavonDeath = 0;
                EmalonDeath = 0;
                KoralonDeath = 0;
				checkTimer = 0;
				stoned = false;
            }

			void OnPlayerEnter(Player* )
			{
				if (stoned)
				{
					for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
						if (Creature* cr = instance->GetCreature(bossGUIDs[i]))
							if (!cr->IsInCombat())
								cr->RemoveAllAuras();

					stoned = false;
				}
			}

			void Update(uint32 diff)
			{
				checkTimer += diff;
				if (checkTimer >= 60000)
				{
					checkTimer -= 60000; // one minute
					if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG))
					{
						if (!bf->IsWarTime())
						{
							if (bf->GetTimer() <= (16 * MINUTE * IN_MILLISECONDS) && bf->GetTimer() >= (15 * MINUTE * IN_MILLISECONDS))
							{
								Map::PlayerList const &PlayerList = instance->GetPlayers();
								if (!PlayerList.isEmpty())
									for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
										if (Player* player = i->GetSource())
											player->MonsterTextEmote("This instance will reset in 15 minutes.", 0, true);
							}
							else if (bf->GetTimer() <= (10 * MINUTE * IN_MILLISECONDS) && bf->GetTimer() >= (9 * MINUTE * IN_MILLISECONDS))
							{
								for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
									if (Creature* cr = instance->GetCreature(bossGUIDs[i]))
										if (!cr->IsInCombat())
										{
											cr->RemoveAllAuras();
											if (Aura* aur = cr->AddAura(SPELL_STONED_AURA, cr))
											{
												aur->SetMaxDuration(60 * MINUTE* IN_MILLISECONDS);
												aur->SetDuration(60 * MINUTE* IN_MILLISECONDS);
											}
										}

								stoned = true;
							}
							else if (bf->GetTimer() <= (2 * MINUTE * IN_MILLISECONDS) && bf->GetTimer() > (MINUTE * IN_MILLISECONDS))
							{
								Map::PlayerList const &PlayerList = instance->GetPlayers();
								if (!PlayerList.isEmpty())
									for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
										if (Player* player = i->GetSource())
											player->MonsterTextEmote("This instance is about to reset. Prepare to be removed.", 0, true);
							}
							else if (bf->GetTimer() <= MINUTE * IN_MILLISECONDS)
							{
								for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
									if (Creature* cr = instance->GetCreature(bossGUIDs[i]))
										if (cr->IsInCombat() && cr->AI())
											cr->AI()->EnterEvadeMode();

								Map::PlayerList const &PlayerList = instance->GetPlayers();
								if (!PlayerList.isEmpty())
									for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
										if (Player* player = i->GetSource())
											player->TeleportTo(player->m_homebindMapId, player->m_homebindX, player->m_homebindY, player->m_homebindZ, player->GetOrientation());
							}
						}
					}
				}
			}

			bool IsEncounterInProgress() const
			{
				for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
					if (m_auiEncounter[i] == IN_PROGRESS)
						return true;

				Battlefield* bf = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG);
				if (!bf || bf->IsWarTime() || bf->GetTimer() <= 10 * MINUTE * IN_MILLISECONDS)
					return true;

				return false;
			}

            void OnCreatureCreate(Creature* creature)
            {
                switch (creature->GetEntry())
                {
                    case CREATURE_TORAVON:
                        bossGUIDs[EVENT_TORAVON] = creature->GetGUID();
                        break;
					case CREATURE_ARCHAVON:
                        bossGUIDs[EVENT_ARCHAVON] = creature->GetGUID();
                        break;
					case CREATURE_KORALON:
                        bossGUIDs[EVENT_KORALON] = creature->GetGUID();
                        break;
					case CREATURE_EMALON:
                        bossGUIDs[EVENT_EMALON] = creature->GetGUID();
                        break;
                }
            }

            uint64 GetData64(uint32 identifier) const
            {
                if (identifier < MAX_ENCOUNTER)
                    return bossGUIDs[identifier];
                return 0;
            }

			uint32 GetData(uint32 identifier) const
			{
				if (identifier == DATA_STONED)
					return (uint32)stoned;
				return 0;
			}

			void SetData(uint32 type, uint32 data)
			{
				switch(type)
				{
					case EVENT_ARCHAVON:
					case EVENT_EMALON:
					case EVENT_KORALON:
					case EVENT_TORAVON:
						m_auiEncounter[type] = data;
						break;
				}

				if (data == DONE)
				{
					SaveToDB();
					switch (type)
					{
						case EVENT_ARCHAVON:
							ArchavonDeath = time(NULL);
							break;
						case EVENT_EMALON:
							EmalonDeath = time(NULL);
							break;
						case EVENT_KORALON:
							KoralonDeath = time(NULL);
							break;
						default:
							return;
					}

					// on every death of Archavon, Emalon and Koralon check our achievement
					DoCastSpellOnPlayers(SPELL_EARTH_WIND_FIRE_ACHIEVEMENT_CHECK);
				}
			}

            bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/)
            {
                switch (criteria_id)
                {
                    case CRITERIA_EARTH_WIND_FIRE_10:
                    case CRITERIA_EARTH_WIND_FIRE_25:
                        if (ArchavonDeath && EmalonDeath && KoralonDeath)
                        {
                            // instance difficulty check is already done in db (achievement_criteria_data)
                            // int() for Visual Studio, compile errors with abs(time_t)
                            return (abs(int(ArchavonDeath-EmalonDeath)) < MINUTE && \
                                abs(int(EmalonDeath-KoralonDeath)) < MINUTE && \
                                abs(int(KoralonDeath-ArchavonDeath)) < MINUTE);
                        }
                        break;
                    default:
                        break;
                }

                return false;
            }

			std::string GetSaveData()
			{
				OUT_SAVE_INST_DATA;

				std::ostringstream saveStream;
				saveStream << "V O A " << m_auiEncounter[0] << ' ' << m_auiEncounter[1] << ' ' << m_auiEncounter[2] << ' ' << m_auiEncounter[3];

				OUT_SAVE_INST_DATA_COMPLETE;
				return saveStream.str();
			}

			void Load(const char* in)
			{
				if (!in)
				{
					OUT_LOAD_INST_DATA_FAIL;
					return;
				}

				OUT_LOAD_INST_DATA(in);

				char dataHead1, dataHead2, dataHead3;
				std::istringstream loadStream(in);
				loadStream >> dataHead1 >> dataHead2 >> dataHead3;

				if (dataHead1 == 'V' && dataHead2 == 'O' && dataHead3 == 'A')
				{
					for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
					{
						loadStream >> m_auiEncounter[i];
						if (m_auiEncounter[i] == IN_PROGRESS)
							m_auiEncounter[i] = NOT_STARTED;
					}

					OUT_LOAD_INST_DATA_COMPLETE;
				}
				else
					OUT_LOAD_INST_DATA_FAIL;
			}

			private:
				time_t ArchavonDeath;
				time_t EmalonDeath;
				time_t KoralonDeath;
				uint32 checkTimer;
				bool stoned;

				uint32 m_auiEncounter[MAX_ENCOUNTER];
				uint64 bossGUIDs[MAX_ENCOUNTER];
        };

        InstanceScript* GetInstanceScript(InstanceMap* map) const
        {
            return new instance_vault_of_archavon_InstanceMapScript(map);
        }
};

void AddSC_instance_vault_of_archavon()
{
    new instance_vault_of_archavon();
}
